#include "SpriteAnimation.hpp"
#include "AssetManager.hpp"
#include "Path.hpp"
#include "XmlUtil.hpp"
#include "Texture.hpp"

#include "tinyxml/tinyxml.h"

namespace fge
{

SpriteAnimation::SpriteAnimation(const std::string &filename, AssetManager *assetManager)
{
	TiXmlDocument xml(assetManager->GetAssetPath() + filename);
	textureOwner = false;

	std::string directory = Path::GetDirectoryName(filename);

	bool isLoaded = xml.LoadFile();
	if(isLoaded)
	{
		TiXmlElement *animElement = xml.FirstChildElement("SpriteAnimation");

		frameSeconds = StringToNumber<float>(animElement->FirstChildElement("FrameSeconds")->FirstChild()->Value());
		loop = strcmp(animElement->FirstChildElement("Loop")->FirstChild()->Value(), "true") == 0;

		int frameCount = fge::GetChildCount(animElement, "Frame");
		TiXmlNode *textureNode = animElement->FirstChild("Frame");
		for(int i=0; i<frameCount; i++)
		{
			std::string texturePath = Path::CombinePaths(directory, textureNode->FirstChild()->Value());

			TextureRegion textureRegion;
			Texture *texture = assetManager->LoadTexture(texturePath, &textureRegion);
			frames.push_back(std::pair<TextureRegion, Texture*>(textureRegion, texture));
			textureNode = textureNode->NextSibling("Frame");
		}
	}
}

SpriteAnimation::~SpriteAnimation()
{
	if(textureOwner)
	{
		int textureCount = static_cast<int>(frames.size());
		for(int i=0; i<textureCount; i++)
		{
			delete frames[i].second;
		}
	}
}

SpriteAnimation* SpriteAnimation::FromFile(const std::string &filename)
{
	SpriteAnimation *spriteAnimation = new SpriteAnimation();
	spriteAnimation->textureOwner = true;

	std::string directory = Path::GetDirectoryName(filename);

	TiXmlDocument xml(filename);

	bool isLoaded = xml.LoadFile();
	if(isLoaded)
	{
		TiXmlElement *animElement = xml.FirstChildElement("SpriteAnimation");

		spriteAnimation->frameSeconds = StringToNumber<float>(animElement->FirstChildElement("FrameSeconds")->FirstChild()->Value());
		spriteAnimation->loop = strcmp(animElement->FirstChildElement("Loop")->FirstChild()->Value(), "true") == 0;

		int frameCount = fge::GetChildCount(animElement, "Frame");
		TiXmlNode *textureNode = animElement->FirstChild("Frame");
		for(int i=0; i<frameCount; i++)
		{
			std::string texturePath = Path::CombinePaths(directory, textureNode->FirstChild()->Value());

			TextureRegion textureRegion;
			Texture *texture = Texture::FromFile(texturePath, &textureRegion);
			spriteAnimation->frames.push_back(std::pair<TextureRegion, Texture*>(textureRegion, texture));
			textureNode = textureNode->NextSibling("Frame");
		}
	}
	else
	{
		delete spriteAnimation;
		return NULL;
	}

	return spriteAnimation;
}

SpriteAnimationPlayer::SpriteAnimationPlayer()
	: currentAnimation(NULL)
{
}

SpriteAnimationPlayer::~SpriteAnimationPlayer()
{
}

void SpriteAnimationPlayer::AddAnimation(const std::string &name, SpriteAnimation *spriteAnimation)
{
	animations[name] = spriteAnimation;

	if(animations.size() == 1)
	{
		currentAnimation = spriteAnimation;
		currentAnimationName = name;
		currentAnimationTime = 0;
		currentFrame = 0;
	}
}

void SpriteAnimationPlayer::StartAnimation(const std::string &name, bool allowRestart)
{
	if(!allowRestart && name == currentAnimationName)
	{
		return;
	}

	currentAnimationName = name;
	currentAnimation = animations[name];
	currentAnimationTime = 0;
}

AnimationEvent SpriteAnimationPlayer::Update(float deltaSeconds)
{
	AnimationEvent animationEvent;
	animationEvent.AnimationComplete = false;

	currentAnimationTime += deltaSeconds;

	float animationLength = currentAnimation->frames.size() * currentAnimation->frameSeconds;
	while(currentAnimationTime >= animationLength)
	{
		if(currentAnimation->loop)
		{
			currentAnimationTime -= animationLength;
		}
		else
		{
			currentAnimationTime = animationLength - currentAnimation->frameSeconds;
			animationEvent.AnimationComplete = true;
		}
	}

	currentFrame = static_cast<int>(currentAnimationTime / currentAnimation->frameSeconds);

	// todo: if current frame is not the same as started, check for animation triggers

	return animationEvent;
}

AnimationEvent SpriteAnimationPlayer::SetTime(float seconds)
{
	AnimationEvent animationEvent;
	animationEvent.AnimationComplete = false;

	currentAnimationTime = seconds;

	float animationLength = currentAnimation->frames.size() * currentAnimation->frameSeconds;
	while(currentAnimationTime >= animationLength)
	{
		if(currentAnimation->loop)
		{
			currentAnimationTime -= animationLength;
		}
		else
		{
			currentAnimationTime = animationLength - currentAnimation->frameSeconds;
			animationEvent.AnimationComplete = true;
		}
	}

	if(currentAnimationTime < 0)
		currentAnimationTime = 0;

	currentFrame = static_cast<int>(currentAnimationTime / currentAnimation->frameSeconds);

	// todo: if current frame is not the same as started, check for animation triggers

	return animationEvent;
}

void SpriteAnimationPlayer::Draw(SpriteBatch *spriteBatch, const Vector2 &position, float rotation,
	const Vector2 &scale, const Color &tintColor, bool flipHorizontal, bool flipVertical)
{
	spriteBatch->Draw(currentAnimation->frames[currentFrame].second, position,
		&currentAnimation->frames[currentFrame].first.rect, 
		currentAnimation->frames[currentFrame].first.origin, rotation, tintColor, scale);
}

}
